﻿using System;
using System.ComponentModel;
using System.ServiceModel;
using System.Windows;
using ClimateControl.Infrastructure;
using System.Threading;
using System.Windows.Threading;
using Microsoft.Practices.Prism.Commands;
using System.Collections.Generic;
using System.Linq;
using ClimateControl.Infrastructure.Extensions;
using ClimateControl.Client.Properties;
using ClimateControl.Controls;
using System.Collections.ObjectModel;
using ClimateControl.Infrastructure.Service;
using log4net;
using System.Collections;

namespace ClimateControl.Client.ViewModel
{
    class MainViewModel : BaseViewModel, IMainViewModel, IDisposable, IClimateInfo
    {
        #region Fields

        private ClimateClient client;
        private DispatcherTimer progressTimer;
        private DispatcherTimer updateTimer;
        private DispatcherTimer reconnectTimer;
        private string serviceLocation;
        private double secondsSinceVoting;
        private static readonly ILog log = LogManager.GetLogger(typeof(MainViewModel));

        #endregion Fields

        #region Properties

        private State _state;
        public State State
        {
            get { return _state; }
            set { SetAndNotify(ref _state, value); }
        }

        private bool _isAirConOn;
        public bool IsAirConOn
        {
            get { return _isAirConOn; }
            set { SetAndNotify(ref _isAirConOn, value); }
        }

        private int _temperature;
        public int Temperature
        {
            get { return _temperature; }
            set { SetAndNotify(ref _temperature, value); }
        }

        private int _voteExpiry;
        public int VoteExpiry
        {
            get { return _voteExpiry; }
            set { SetAndNotify(ref _voteExpiry, value); }
        }

        private bool _isBigFanOn;
        public bool IsBigFanOn
        {
            get { return _isBigFanOn; }
            set { SetAndNotify(ref _isBigFanOn, value); }
        }

        private double _voteFrequency;
        public double VoteFrequency
        {
            get { return _voteFrequency; }
            set
            {
                _voteFrequency = value;
                if (_voteFrequency > 0)
                    SetStateAccordingToLastVote();
            }
        }

        private int _updateFrequency;
        public int UpdateFrequency
        {
            get { return _updateFrequency; }
            set
            {
                _updateFrequency = value;
                updateTimer.Stop();
                updateTimer.Interval = TimeSpan.FromMinutes(_updateFrequency);
                updateTimer.Start();
            }
        }

        private List<Vote> _votes = new List<Vote>();
        public List<Vote> Votes
        {
            get { return _votes; }
            set
            {
                SetAndNotifyPlusOthers(ref _votes, value,
                    () => TooHotVotes, () => TooColdVotes, () => PerfectVotes, () => MyLastVote);
                SetStateAccordingToLastVote();
            }
        }

        public Vote MyLastVote
        {
            get { return Votes.OrderByDescending(v => v.Time).FirstOrDefault(uv => uv.User == Environment.UserName); }
        }

        public IEnumerable<Vote> TooColdVotes
        {
            get { return Votes.Where(v => v.VoteOption == VoteOption.TooCold); }
        }

        public IEnumerable<Vote> TooHotVotes
        {
            get { return Votes.Where(v => v.VoteOption == VoteOption.TooHot); }
        }

        public IEnumerable<Vote> PerfectVotes
        {
            get { return Votes.Where(v => v.VoteOption == VoteOption.Perfect); }
        }

        #endregion Properties

        #region Commands

        private DelegateCommand _tooHotCommand;
        public DelegateCommand TooHotCommand
        {
            get { return _tooHotCommand; }
            private set { SetAndNotify(ref _tooHotCommand, value); }
        }

        private DelegateCommand _tooColdCommand;
        public DelegateCommand TooColdCommand
        {
            get { return _tooColdCommand; }
            private set { SetAndNotify(ref _tooColdCommand, value); }
        }

        private DelegateCommand _perfectCommand;
        public DelegateCommand PerfectCommand
        {
            get { return _perfectCommand; }
            private set { SetAndNotify(ref _perfectCommand, value); }
        }

        #endregion Commands

        #region Constructor

        public MainViewModel()
        {
            serviceLocation = Settings.Default.ServiceLocation;

            SetupTimers();

            Settings.Default.SettingsSaving += (s, e) =>
            {
                if (Settings.Default.ServiceLocation != serviceLocation)
                {
                    serviceLocation = Settings.Default.ServiceLocation;
                    StartBackgroundThread(OpenService);
                }
            };

            TooHotCommand = new DelegateCommand(() => SendVote(VoteOption.TooHot));
            TooColdCommand = new DelegateCommand(() => SendVote(VoteOption.TooCold));
            PerfectCommand = new DelegateCommand(() => SendVote(VoteOption.Perfect));

            StartBackgroundThread(OpenService);
        }

        #endregion Constructor

        #region Methods

        private void SetupTimers()
        {
            reconnectTimer = new DispatcherTimer();
            reconnectTimer.Interval = TimeSpan.FromSeconds(10);
            reconnectTimer.Tick += (s, e) => StartBackgroundThread(OpenService);

            progressTimer = new DispatcherTimer();
            progressTimer.Interval = TimeSpan.FromSeconds(1);
            progressTimer.Tick += new EventHandler(progressTimer_Tick);            

            updateTimer = new DispatcherTimer();
            updateTimer.Interval = TimeSpan.FromMinutes(30);
            updateTimer.Tick += updateTimer_Tick;
            updateTimer.Start();
        }

        private double _progress;
        public double Progress
        {
            get { return _progress; }
            set { SetAndNotify(ref _progress, value); }
        }

        public string ProgressToolTip
        {
            get { return string.Format("{0} / {1}", TimeSpan.FromSeconds(secondsSinceVoting).ToString(@"mm\:ss"), TimeSpan.FromMinutes(VoteFrequency).ToString(@"mm\:ss")); }
        }
      
        public void OpenService()
        {
            try
            {
                reconnectTimer.Stop();

                State = State.Connecting;

                InstanceContext instanceContext = new InstanceContext(this);
                instanceContext.Faulted += TryReconnect;

                client = new ClimateClient(instanceContext, TryReconnect, serviceLocation);

                ClimateInfo climateInfo = client.GetClimateInfo();

                Votes = climateInfo.Votes;
                VoteFrequency = climateInfo.VoteFrequency;
                IsAirConOn = climateInfo.IsAirConOn;
                Temperature = climateInfo.Temperature;
                VoteExpiry = climateInfo.VoteExpiry;
                IsBigFanOn = climateInfo.IsBigFanOn;
            }
            catch (Exception ex)
            {
                log.Error(ex.Message);
                TryReconnect();
            }
        }

        private void TryReconnect(object sender = null, EventArgs e = null)
        {
            if (!reconnectTimer.IsEnabled)
            {
                reconnectTimer.Start();
            }
        }

        private void SetStateAccordingToLastVote()
        {
            progressTimer.Stop();

            if (MyLastVote != null && MyLastVote.Time.IsInTheLast(VoteFrequency).Minutes)
            {
                double secondsTilICanVote = (MyLastVote.Time.AddMinutes(VoteFrequency) - DateTime.Now).TotalSeconds;             
                secondsSinceVoting = TimeSpan.FromMinutes(VoteFrequency).TotalSeconds - secondsTilICanVote;
                progressTimer.Start();
                State = State.VoteSent;
            }
            else
            {
                State = State.Ready;
            }
        }

        private void SendVote(VoteOption voteOption)
        {
            secondsSinceVoting = 0;
            Progress = 1;
            State = State.SendingVote;

            StartBackgroundThread(() =>
            {
                try
                {
                    client.Vote(voteOption, Environment.UserName);
                    State = State.VoteSent;
                }
                catch (Exception ex)
                {
                    log.Error(ex.Message);
                    TryReconnect();
                }
            });
        }

        #endregion Methods

        #region Events

        private void progressTimer_Tick(object sender, EventArgs e)
        {
            secondsSinceVoting += progressTimer.Interval.TotalSeconds;

            double progress = 0;

            if (VoteFrequency > 0)
                progress = secondsSinceVoting / TimeSpan.FromMinutes(VoteFrequency).TotalSeconds;

            Progress = 1 - progress;

            if (progress >= 1)
            {
                progressTimer.Stop();
                State = State.Ready;
            }
        }
              
        private void updateTimer_Tick(object sender, EventArgs e)
        {
            updateTimer.Stop();
            UpdateManager.CheckForUpdate();
            updateTimer.Start();
        }

        #endregion Events

        #region IDiposable

        public void Dispose()
        {
            State = State.ShuttingDown;
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        protected virtual void Dispose(bool disposing)
        {
            if (disposing)
            {
                if (client != null)
                {
                    try
                    {
                        client.Close();
                    }
                    catch (Exception ex)
                    {
                        log.Error(ex.Message);
                        client.Abort();
                    }
                    client = null;
                }
            }
        }

        #endregion
    }
}